package jp.gnt.swf;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.InflaterInputStream;

public class SWFHeader {
	public int version;
	public int frameRate;
	public int frameCount;
	public long fileLength; // total lenght of file in bytes
	public long headerLength; // total length of header in bytes
	public boolean isCompressed;
	public byte[] signature;
	public RECT frameSize; // size of flash screen

	/**
	 * 
	 * @param filePath
	 * @return
	 * @throws IOException
	 */
	public void parseHeader(InputStream ins) throws IOException {
		byte[] bytes = new byte[1024];
		this.signature = new byte[3];

		ins.read(signature, 0, 3);

		if ((this.signature[1] & SWFUtils.maskInt) != 0x57 || (this.signature[2] & SWFUtils.maskInt) != 0x53) {
			throw new IOException(">>>> SWF file format invalid....");
		}

		ins.read(bytes, 0, 1);
		this.version = bytes[0] & SWFUtils.maskInt;

		ins.read(bytes, 0, 4);
		this.fileLength = SWFService.convertByte2Long(bytes, 4);

		if ((this.signature[1] & SWFUtils.maskInt) == 0x43) {
			isCompressed = true;
			ins = new InflaterInputStream(ins);
		}

		ins.read(bytes, 0, 1);
		int nBits = (bytes[0] & SWFUtils.maskInt) >> 3;
		int frameSizeBytes = ((nBits * 4 - 3) % 8) == 0 ? ((nBits * 4 - 3) / 8) : ((nBits * 4 - 3) / 8) + 1;

		ins.read(bytes, 1, frameSizeBytes);
		this.frameSize = getFrameSize(bytes, nBits);

		ins.read(bytes, 0, 2);
		this.frameRate = bytes[1] & SWFUtils.maskInt; // The first byte not
														// available

		ins.read(bytes, 0, 2);
		this.frameCount = SWFService.convertByte2Int(bytes, 2);

		// Total length of header data
		headerLength = 8 + (1 + frameSizeBytes) + 2 + 2;
	}

	/**
	 * 
	 * @param bytes
	 * @param nBits
	 * @return
	 */
	private static RECT getFrameSize(byte[] bytes, int nBits) {
		int numBitPerByte = 0x08;
		int numFields = 0x04;
		int dimIdx = 0;
		int dims[] = new int[0x04];
		int bitCounter = 0;
		int byteOffset = 0;
		int bitOffset = 0x05; // get last 3 bit of bytes[0]

		while (dimIdx < numFields) {
			while (bitCounter < nBits) {
				int cbit = (nBits - bitCounter) > (numBitPerByte - bitOffset) ? (numBitPerByte - bitOffset)
						: (nBits - bitCounter);

				int bitMask = 0x000000ff >> bitOffset;
				dims[dimIdx] = dims[dimIdx] << cbit;
				dims[dimIdx] |= ((bytes[byteOffset] & 0x000000ff) >> (numBitPerByte - bitOffset - cbit)) & bitMask;

				bitOffset += cbit;
				bitCounter += cbit;
				if (bitOffset == numBitPerByte) {
					bitOffset = 0;
					byteOffset++;
				}
			}
			dimIdx++;
			bitCounter = 0;
		}

		return new RECT(nBits, dims[0], dims[1], dims[2], dims[3]);
	}

	/**
	 * 
	 * @return
	 */
	public byte[] toByteArray() {
		byte[] bytes = new byte[1024];
		ByteArrayOutputStream outs = new ByteArrayOutputStream();

		// F[C] W S
		bytes[0] = (byte) (isCompressed ? 0x43 : 0x46);
		bytes[1] = 0x57;
		bytes[2] = 0x53;
		outs.write(bytes, 0, 3);

		// Version
		bytes[0] = (byte) (this.version);
		outs.write(bytes, 0, 1);

		// Length
		bytes = SWFService.convertLong2Byte(this.fileLength);
		outs.write(bytes, 0, 4);

		// Frame size
		byte[] byteArray = this.frameSize.toByteArray();
		outs.write(byteArray, 0, byteArray.length);

		// Frame rate
		bytes[0] = (byte) (0x0000);
		bytes[1] = (byte) (this.frameRate | 0x0000);
		outs.write(bytes, 0, 2);

		// Frame count
		bytes = SWFService.convertInt2Byte(this.frameCount);
		outs.write(bytes, 0, 2);

		return outs.toByteArray();
	}
	
	public boolean isCompressed() {
		return this.isCompressed;
	}
	
	public void updateFileLength(long contentLength) {
		this.fileLength = headerLength + contentLength;
	}
}
